/*
 * Copyright (c) 2008-2014, RF-Embedded GmbH
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 *  1. Redistributions of source code must retain the above copyright notice, 
 *     this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright notice, 
 *     this list of conditions and the following disclaimer in the 
 *     documentation and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
 * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.rfe.protocol;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import com.rfe.JrfeGlobal;
import com.rfe.device.IProtocolDeviceInterface;
import com.rfe.helper.JrfeHelper;

/**
 * The protocol handler
 */
public class JrfeProtocolHandler {

    /**
     * Constructor of a new handler instance.
     * @param device 	The communication device to the reader
     */
    public JrfeProtocolHandler(IProtocolDeviceInterface device)
    {
        _Device = device;
        _Device.setListener(_DeviceListener);
    }
    
    /**
     * Closes the handler and the closes the device.
     */
    public void close()
    {
    	_Device.close();
    }
    

    /********************************************************************************************* Reader-Common*/

    /**
     * Retrieves the reader ID of the reader.
     * 
     * @return Retrieved reader ID
     * @throws JrfeProtocolException 
	 */
    public Integer getReaderID() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader ID - Trying to get Reader ID");

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON, JrfeConstants.RFE_COM2_GET_SERIAL_NUMBER);
        if (!res){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader ID - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_SERIAL_NUMBER), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader ID - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader ID - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        Integer readerID = JrfeHelper.toInteger(payl,  0,  4);
        
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader ID - OK : Reader ID = " + JrfeHelper.toHexString(readerID));

        return readerID;
    }
    
    /**
     * Retrieves the reader type of the reader.
     * 
     * @return Retrieved reader type
     * @throws JrfeProtocolException 
	 */
    public Integer getReaderType() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader Type - Trying to get Reader Type");

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
                JrfeConstants.RFE_COM2_GET_READER_TYPE);
        if (!res){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader Type - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
                JrfeConstants.RFE_COM2_GET_READER_TYPE), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader Type - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader Type - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        Integer readerType = JrfeHelper.toInteger(payl,  0,  4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Reader Type - OK : Reader Type = " + JrfeHelper.toHexString(readerType));

        return readerType;
    }

    /**
     * Retrieves the hardware revision of the reader.
     * 
     * @return Retrieved hardware revision
     * @throws JrfeProtocolException 
	 */
    public Integer getHardwareRevision() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Hardware Rev - Trying to get hardware ID");

        Integer hardwareRevision = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_HARDWARE_REVISION);
        if (!res){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Hardware Rev - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_HARDWARE_REVISION), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Hardware Rev - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Hardware Rev - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        hardwareRevision = JrfeHelper.toInteger(payl,  0,  4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Hardware Rev - OK : Hardware Revision = " + JrfeHelper.toHexString(hardwareRevision));

        return hardwareRevision;
    }

    /**
     * Retrieves the software revision of the reader.
     * 
     * @return Retrieved software revision
     * @throws JrfeProtocolException 
	 */
    public Integer getSoftwareRevision() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Software Rev - Trying to get software ID");

        Integer softwareRevision = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_SOFTWARE_REVISION);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Software Rev - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_SOFTWARE_REVISION), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Software Rev - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Software Rev - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        softwareRevision = JrfeHelper.toInteger(payl,  0,  4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Software Rev - OK : Software Revision = " + JrfeHelper.toHexString(softwareRevision));

        return softwareRevision;
    }

    /**
     * Retrieves the bootloader revision of the reader.
     * 
     * @return Retrieved bootloader revision
     * @throws JrfeProtocolException 
	 */
    public Integer getBootloaderRevision() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Bootloader Rev - Trying to get bootloader ID");

        Integer bootloaderRevision = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_BOOTLOADER_REVISION);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Bootloader Rev - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_BOOTLOADER_REVISION), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Bootloader Rev - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Bootloader Rev - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        bootloaderRevision = JrfeHelper.toInteger(payl,  0,  4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Bootloader Rev - OK : Bootloader Revision = " + JrfeHelper.toHexString(bootloaderRevision));

        return bootloaderRevision;
    }

    /**
     * Retrieves the current running system of the reader.
     * 
     * @return The current running sytem of the reader
     * @throws JrfeProtocolException 
	 */
    public Integer getCurrentSystem() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current System - Trying to get current System");

        Integer currentSystem = JrfeConstants.eRFE_CURRENT_SYSTEM.RFE_SYS_FIRMWARE;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_CURRENT_SYSTEM);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current System - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_CURRENT_SYSTEM), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current System - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 1){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current System - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        currentSystem = JrfeHelper.toInteger(payl,  0,  1);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current System - OK : System " + JrfeHelper.toHexString(currentSystem));

        return currentSystem;
    }

    /**
     * Retrieves the current state of the reader.
     * 
     * @return Current state of the reader
     * @throws JrfeProtocolException 
	 */
    public Integer getCurrentState() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current State - Trying to get current State");
        
        Integer currentState = JrfeConstants.eRFE_CURRENT_READER_STATE.RFE_STATE_IDLE;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_CURRENT_STATE);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current State - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_CURRENT_STATE), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current State - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 1){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current State - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        currentState = JrfeHelper.toInteger(payl,  0,  1);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Current State - OK : State " + JrfeHelper.toHexString(currentState));

        return currentState;
    }

    /**
     * Retrieves the status register of the reader.
     * 
     * @return Status register of the reader
     * @throws JrfeProtocolException 
	 */
    public Long getStatusRegister (  ) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Status Register - Trying to get current State");

        Long statusRegister = 0L;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_STATUS_REGISTER);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Status Register - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
		        JrfeConstants.RFE_COM2_GET_STATUS_REGISTER), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Status Register - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 8){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Status Register - NOK - Payl");
	        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }
        

        statusRegister = JrfeHelper.toLong(payl,  0,  8);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Status Register - OK : Status Register = " + JrfeHelper.toHexString(statusRegister));

        return statusRegister;
    }

    /**
     * Retrieves the antenna count of the reader.
     * 
     * @return Retrieved antenna count of the reader
     * @throws JrfeProtocolException 
	 */
    public Integer getAntennaCount() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Antenna Count - Trying to get antenna count");

        Integer count = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_COMMON,
                JrfeConstants.RFE_COM2_GET_ANTENNA_COUNT);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Antenna Count - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_COMMON,
                JrfeConstants.RFE_COM2_GET_ANTENNA_COUNT), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Antenna Count - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 2)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Antenna Count - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        count = JrfeHelper.toInteger(payl,  0,  2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Antenna Count - OK : Count " + JrfeHelper.toHexString(count));

        return count;
    }

    
    
    /********************************************************************************************* Reader-RF*/
    /**
     * Retrieves the maximum attenuation settings of the reader.
     * @return Maximum settable value for the attenuation
     * @throws JrfeProtocolException
     */
    public Short getMaxAttenuation() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - Trying to attenuation");

        Short maxAttenuation = 0;
        Short currentAttenuation = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_ATTENUATION);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_ATTENUATION), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 5 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Payl");
	        _LastReturnCode = (byte) payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        maxAttenuation = JrfeHelper.toShort(payl, 1, 2);
        currentAttenuation = JrfeHelper.toShort(payl, 3, 2);;

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - OK : Max(" + maxAttenuation +
							         ") - Cur(" + currentAttenuation + ")");

        return maxAttenuation;
    }
    
    /**
     * Retrieves the set attenuation settings of the reader.
     * @return Current set value for the attenuation
     * @throws JrfeProtocolException
     */
    public Short getCurrentAttenuation() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - Trying to attenuation");

        Short maxAttenuation = 0;
        Short currentAttenuation = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_ATTENUATION);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_ATTENUATION), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 5 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - NOK - Payl");
	        _LastReturnCode = (byte) payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        maxAttenuation = JrfeHelper.toShort(payl, 1, 2);
        currentAttenuation = JrfeHelper.toShort(payl, 3, 2);;

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Attenuation - OK : Max(" + maxAttenuation +
							         ") - Cur(" + currentAttenuation + ")");

        return currentAttenuation;
    }

    /**
     * Retrieves the current set frequency mode of the reader.
     * @return Current used mode
     * @throws JrfeProtocolException
     */
    public Byte getFrequencyMode() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - Trying to frequency");

        Byte mode = 0;
        Byte maxFrequencyCount = 0;
        List<Integer> frequencies = new ArrayList<Integer>();

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length < 5 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Payl");
	        _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        mode = payl[1];
        maxFrequencyCount = payl[2];
        byte count = payl[3];
        byte index = 4;
        for (int i = 0; i < count; i++)
        {
	        Integer freq = JrfeHelper.toInteger(payl, index, 3);
	        index += 3;
	        frequencies.add(freq);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK : Mode(" + mode + ") - Max(" + maxFrequencyCount + ")");
        for(int i = 0; i < frequencies.size(); i++){
        	Integer f = frequencies.get(i);
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK :     Freq(" + f + "kHz)");
        }

        return mode;
    }
    
    /**
     * Retrieves the maximum frequency table size of the reader.
     * @return Maximum count of frequency table entries
     * @throws JrfeProtocolException
     */
    public Byte getMaxFrequencyCount() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - Trying to frequency");

        Byte mode = 0;
        Byte maxFrequencyCount = 0;
        List<Integer> frequencies = new ArrayList<Integer>();

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length < 5 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        mode = payl[1];
        maxFrequencyCount = payl[2];
        byte count = payl[3];
        byte index = 4;
        for (int i = 0; i < count; i++)
        {
	        Integer freq = JrfeHelper.toInteger(payl, index, 3);
	        index += 3;
	        frequencies.add(freq);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK : Mode(" + mode + ") - Max(" + maxFrequencyCount + ")");
        for(int i = 0; i < frequencies.size(); i++){
        	Integer f = frequencies.get(i);
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK :     Freq(" + f + "kHz)");
        }

        return maxFrequencyCount;
    }
    
    /**
     * Retrieves the frequency table of the reader.
     * @return Frequency table
     * @throws JrfeProtocolException
     */
    public List<Integer> getFrequencyList() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - Trying to frequency");

        Byte mode = 0;
        Byte maxFrequencyCount = 0;
        List<Integer> frequencies = new ArrayList<Integer>();

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_FREQUENCY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length < 5 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        mode = payl[1];
        maxFrequencyCount = payl[2];
        byte count = payl[3];
        byte index = 4;
        for (int i = 0; i < count; i++)
        {
	        Integer freq = JrfeHelper.toInteger(payl, index, 3);
	        index += 3;
	        frequencies.add(freq);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK : Mode(" + mode + ") - Max(" + maxFrequencyCount + ")");
        for(int i = 0; i < frequencies.size(); i++){
        	Integer f = frequencies.get(i);
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Frequency - OK :     Freq(" + f + "kHz)");
        }

        return frequencies;
    }

    /**
     * Retrieves the maximal sensitivity settings of the reader.
     * @return Maximum settable sensitivity
     * @throws JrfeProtocolException
     */
    public Short getMaxSensitivity( ) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - Trying to get Sensitivity");

        Short maxSensitivity = 0;
        Short minSensitivity = 0;
        Short currentSensitivity = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 7 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        maxSensitivity = JrfeHelper.toShort(payl, 1, 2);
        minSensitivity = JrfeHelper.toShort(payl, 3, 2);
        currentSensitivity = JrfeHelper.toShort(payl, 5, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - OK : Max(" + maxSensitivity +
							         ") - Min(" + minSensitivity +
							         ") - Cur" + currentSensitivity + ")");

        return maxSensitivity;
    }
    
    /**
     * Retrieves the minimal sensitivity settings of the reader.
     * @return Mininim settable sensitivity
     * @throws JrfeProtocolException
     */
    public Short getMinSensitivity( ) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - Trying to get Sensitivity");

        Short maxSensitivity = 0;
        Short minSensitivity = 0;
        Short currentSensitivity = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 7 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        maxSensitivity = JrfeHelper.toShort(payl, 1, 2);
        minSensitivity = JrfeHelper.toShort(payl, 3, 2);
        currentSensitivity = JrfeHelper.toShort(payl, 5, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - OK : Max(" + maxSensitivity +
							         ") - Min(" + minSensitivity +
							         ") - Cur" + currentSensitivity + ")");

        return minSensitivity;
    }
    
    /**
     * Retrieves the current set sensitivity settings of the reader.
     * @return Current set sensitivity
     * @throws JrfeProtocolException
     */
    public Short getSensitivity( ) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - Trying to get Sensitivity");

        Short maxSensitivity = 0;
        Short minSensitivity = 0;
        Short currentSensitivity = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
		        JrfeConstants.RFE_COM2_GET_SENSITIVITY), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 7 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        maxSensitivity = JrfeHelper.toShort(payl, 1, 2);
        minSensitivity = JrfeHelper.toShort(payl, 3, 2);
        currentSensitivity = JrfeHelper.toShort(payl, 5, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Sensitivity - OK : Max(" + maxSensitivity +
							         ") - Min(" + minSensitivity +
							         ") - Cur" + currentSensitivity + ")");

        return currentSensitivity;
    }

    /**
     * Retrieves the LBT param Listen Timer of the reader.
     * @return Listen time in msecs
     * @throws JrfeProtocolException
     */
    public Short getLbtListenTime() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - Trying to get Lbt Params");

        Short listenTime = 0;
        Short idleTime = 0;
        Short maxAllocTime = 0;
        Short rssiThreshold = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 9 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Payl");
            _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        listenTime = JrfeHelper.toShort(payl, 1, 2);
        idleTime = JrfeHelper.toShort(payl, 3, 2);
        maxAllocTime = JrfeHelper.toShort(payl, 5, 2);
        rssiThreshold = JrfeHelper.toShort(payl, 7, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - OK : Listen Time " + listenTime +
                                     " Idle Time " + idleTime +
                                     " Maximum Allocation Time " + maxAllocTime +
                                     " RSSI Threshold " + rssiThreshold);

        return listenTime;
    }

    /**
     * Retrieves the LBT param Idle Time of the reader.
     * @return Idle time in msecs
     * @throws JrfeProtocolException
     */
    public Short getLbtIdleTime() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - Trying to get Lbt Params");

        Short listenTime = 0;
        Short idleTime = 0;
        Short maxAllocTime = 0;
        Short rssiThreshold = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 9 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Payl");
            _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        listenTime = JrfeHelper.toShort(payl, 1, 2);
        idleTime = JrfeHelper.toShort(payl, 3, 2);
        maxAllocTime = JrfeHelper.toShort(payl, 5, 2);
        rssiThreshold = JrfeHelper.toShort(payl, 7, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - OK : Listen Time " + listenTime +
                                     " Idle Time " + idleTime +
                                     " Maximum Allocation Time " + maxAllocTime +
                                     " RSSI Threshold " + rssiThreshold);

        return idleTime;
    }

    /**
     * Retrieves the LBT param Maximum Allocation Time of the reader.
     * @return Maximum allocation time in msecs
     * @throws JrfeProtocolException
     */
    public Short getLbtMaxAllocationTime() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - Trying to get Lbt Params");

        Short listenTime = 0;
        Short idleTime = 0;
        Short maxAllocTime = 0;
        Short rssiThreshold = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 9 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Payl");
            _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        listenTime = JrfeHelper.toShort(payl, 1, 2);
        idleTime = JrfeHelper.toShort(payl, 3, 2);
        maxAllocTime = JrfeHelper.toShort(payl, 5, 2);
        rssiThreshold = JrfeHelper.toShort(payl, 7, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - OK : Listen Time " + listenTime +
                                     " Idle Time " + idleTime +
                                     " Maximum Allocation Time " + maxAllocTime +
                                     " RSSI Threshold " + rssiThreshold);

        return maxAllocTime;
    }

    /**
     * Retrieves the LBT param RSSI Threshold of the reader.
     * @return RSSI Threshold
     * @throws JrfeProtocolException
     */
    public Short getLbtRSSIthreshold() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - Trying to get Lbt Params");

        Short listenTime = 0;
        Short idleTime = 0;
        Short maxAllocTime = 0;
        Short rssiThreshold = 0;

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_GET_LBT_PARAMS), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length != 9 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - NOK - Payl");
            _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        // set the variable to the new value
        listenTime = JrfeHelper.toShort(payl, 1, 2);
        idleTime = JrfeHelper.toShort(payl, 3, 2);
        maxAllocTime = JrfeHelper.toShort(payl, 5, 2);
        rssiThreshold = JrfeHelper.toShort(payl, 7, 2);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Lbt Params - OK : Listen Time " + listenTime +
                                     " Idle Time " + idleTime +
                                     " Maximum Allocation Time " + maxAllocTime +
                                     " RSSI Threshold " + rssiThreshold);

        return rssiThreshold;
    }

    
    /**
     * Sets the attenuation setting of the reader
     * @param value 	The new attenuation value
     * @throws JrfeProtocolException
     */
    public void setAttenuation(Short value) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Set Attenuation - Trying to set output power to " + value);

        byte[] payload;
        payload = new byte[2];
        payload[0] = (byte)(value >> 8);
        payload[1] = (byte)(value >> 0);

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_SET_ATTENUATION, payload);
        if (res != true)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Set Attenuation - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_RF,
                JrfeConstants.RFE_COM2_SET_ATTENUATION), _ResponseTimeOut, result);
        if (!result.get())
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Set Attenuation - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            JrfeGlobal.trc(_MinimumTraceLevel, "Set Attenuation - NOK - Payl");
            _LastReturnCode = payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Set Attenuation - OK");

        return;
    } 
    
    /********************************************************************************************* Reader-Control */

    /**
     * Reboots the reader
     * @throws JrfeProtocolException
     * @throws InterruptedException 
     */
    public void reboot() throws JrfeProtocolException, InterruptedException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Reboot - Trying to reboot");

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_REBOOT);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Reboot - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        Thread.sleep(50);

        return;
    }

    /**
     * Sets the heartbeat settings of the reader
     * @param on			Specifies if the reader should send a heartbeat
     * @param interval		Specifies the interval of the heartbeat
     * @throws JrfeProtocolException
     */
    public void setHeartBeat(boolean on, Short interval) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Set Heartbeat - Trying to set heartbeat " + ((on) ? "ON" : "OFF"));

        byte[] payload = new byte[0];
        if (interval == 0)
        {
            payload = new byte[1];
            payload[0] = (byte)((on) ? JrfeConstants.eRFE_HEARTBEAT_SIGNAL.HEARTBEAT_ON : JrfeConstants.eRFE_HEARTBEAT_SIGNAL.HEARTBEAT_OFF);
        }
        if (interval != 0)
        {
	        payload = new byte [3];
            payload[0] = (byte)((on) ? JrfeConstants.eRFE_HEARTBEAT_SIGNAL.HEARTBEAT_ON : JrfeConstants.eRFE_HEARTBEAT_SIGNAL.HEARTBEAT_OFF);
	        payload[1] = (byte) (interval >> 8);
	        payload[2] = (byte) (interval >> 0);
        }

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SET_HEARTBEAT, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Heartbeat - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SET_HEARTBEAT), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Heartbeat - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Heartbeat - NOK - Payl");
	        _LastReturnCode =  (byte) payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Set Heartbeat - OK");

        return;
    }

    /**
     * Sets the antenna power of the reader
     * @param on		Specifies if the antenna power should be activated
     * @throws JrfeProtocolException
     */
    public void setAntennaPower(boolean on) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Set Antenna - Trying to set antenna power " + ((on) ? "ON" : "OFF"));

        byte[] payload;
        payload = new byte[1];
        payload[0] = (byte) ((on) ? JrfeConstants.eRFE_ANTENNA_POWER.ANTENNA_ON : JrfeConstants.eRFE_ANTENNA_POWER.ANTENNA_OFF);

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SET_ANTENNA_POWER, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Antenna - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SET_ANTENNA_POWER), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Antenna - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Set Antenna - NOK - Payl");
	        _LastReturnCode =  (byte) payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Set Antenna - OK");

        return;
    }

    /**
     * Saves the settings permanent on the reader
     * @throws JrfeProtocolException
     */
    public void saveSettingsPermanent() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Save Settings - Trying save settings permanent");

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SAVE_SETTINGS_PERMANENT);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Save Settings - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_SAVE_SETTINGS_PERMANENT), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Save Settings - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Save Settings - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Save Settings - OK");

        return;
    }

    /**
     * Restores the factory settings of the reader
     * @throws JrfeProtocolException
     */
    public void restoreFactorySettings() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Restore Settings - Trying to restore settings");

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_RESTORE_FACTORY_SETTINGS);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Restore Settings - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_RESTORE_FACTORY_SETTINGS), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Restore Settings - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Restore Settings - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Restore Settings - OK");

        return;
    }

    /**
     * Retrieves a parameter from the reader at the given address
     * @param address		Address of the paremeter
     * @return				Retrieved value of the parameter
     * @throws JrfeProtocolException
     */
    public byte[] getParam(Short address) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Get Param - Trying to get param of address " + address);

        byte[] value = null;

        byte[] payload;
        payload = new byte[2];
        payload[0] = (byte) (address >> 8);
        payload[1] = (byte) (address >> 0);

        // reset last return code
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_GET_PARAM, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Param - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_CONTROL,
		        JrfeConstants.RFE_COM2_GET_PARAM), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Param - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Get Param - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        value = new byte[payl[1]];
        System.arraycopy(payl, 2, value, 0, value.length);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get Param - OK : " + JrfeHelper.toHexString(value));

        return value;
    }

    /********************************************************************************************* GPIO */
    /**
     * Retrieves the GPIO capabillities of the reader
     * @return Bit mask of available GPIOs
     * @throws JrfeProtocolException
     */
    public Integer getGPIOCaps_Mask() throws JrfeProtocolException {
        Integer mask = 0;

        JrfeGlobal.trc(6, "Get GPIO Caps - Trying to get GPIO Caps");

        // reset the flag
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS);
        if (res != true){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the flag or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS), _ResponseTimeOut, result);
        if (!result.get()){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        if (payl.length != 12){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        mask = JrfeHelper.toInteger(payl, 0, 4);

        JrfeGlobal.trc(6, "Get GPIO Caps - OK");

        return mask;
    }

    /**
     * Retrieves the GPIO capabillities of the reader
     * @return Bit mask of GPIOs that are available as output
     * @throws JrfeProtocolException
     */
    public Integer getGPIOCaps_Output() throws JrfeProtocolException {
        Integer output = 0;

        JrfeGlobal.trc(6, "Get GPIO Caps - Trying to get GPIO Caps");

        // reset the flag
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS);
        if (res != true){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the flag or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS), _ResponseTimeOut, result);
        if (!result.get()){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        if (payl.length != 12){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        output = JrfeHelper.toInteger(payl, 4, 4);

        JrfeGlobal.trc(6, "Get GPIO Caps - OK");

        return output;
    }


    /**
     * Retrieves the GPIO capabillities of the reader
     * @return Bit mask of GPIOs that are available as input
     * @throws JrfeProtocolException
     */
    public Integer getGPIOCaps_Input() throws JrfeProtocolException {
        Integer input = 0;

        JrfeGlobal.trc(6, "Get GPIO Caps - Trying to get GPIO Caps");

        // reset the flag
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS);
        if (res != true){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the flag or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_CAPS), _ResponseTimeOut, result);
        if (!result.get()){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        if (payl.length != 12){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Caps - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        input = JrfeHelper.toInteger(payl, 8, 4);

        JrfeGlobal.trc(6, "Get GPIO Caps - OK");

        return input;
    }


    /**
     * Retrieves the current set GPIO direction
     * @return Bit mask of the current direction, 1 = output, 2 = input
     * @throws JrfeProtocolException
     */
    public Integer getGPIODirection() throws JrfeProtocolException {
        Integer direction = 0;

        JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - Trying to get GPIO Direction");

        // reset the flag
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_DIRECTION);
        if (res != true){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the flag or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO_DIRECTION), _ResponseTimeOut, result);
        if (!result.get()){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        if (payl.length != 4){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        direction = JrfeHelper.toInteger(payl, 0, 4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - OK");

        return direction;
    }

    /**
     * Retrieves the current level of the GPIO pins
     * @return Bit mask of the current level
     * @throws JrfeProtocolException
     */
    public Integer getGPIO() throws JrfeProtocolException {
        Integer mask = 0;

        JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO - Trying to get GPIO");

        // reset the flag
        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO);
        if (res != true){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO - NOK - Send");
            throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the flag or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_READER_GPIO,
                JrfeConstants.RFE_COM2_GET_GPIO), _ResponseTimeOut, result);
        if (!result.get()){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO - NOK - Resp");
            throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        if (payl.length != 4){
            JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO - NOK - Payl");
            throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        mask = JrfeHelper.toInteger(payl, 0, 4);

        JrfeGlobal.trc(_MinimumTraceLevel, "Get GPIO Direction - OK");

        return mask;

    }

    /********************************************************************************************* Tag-Functions */

    /**
     * Executes a single inventory at the reader.
     * @return		List of found tags
     * @throws JrfeProtocolException
     */
    public List<JrfeTagEvent> doSingleInventory() throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - Trying to do an inventory");

        List<JrfeTagEvent> tagList = new ArrayList<JrfeTagEvent>();

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_INVENTORY_SINGLE);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        byte epcCount = 0;
        byte epcsReceived = 0;

        while (true)
        {

            // wait for either the response or a timeout
            AtomicBoolean result = new AtomicBoolean(false);
            byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
                    JrfeConstants.RFE_COM2_INVENTORY_SINGLE), _ResponseTimeOut, result);
            if (!result.get())
            {
                JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - NOK - Resp");
                throw JrfeProtocolException.ReceiveError(_LastSentMessage);
            }

            // parse the response
            if (payl.length == 0 || (payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
            {
                JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - NOK - Payl");
                _LastReturnCode = payl[0];
                if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
    	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
    	        else
    	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
            }

            epcCount = payl[1];
            byte epcsInPackage = payl[2];
            byte index = 3;

            for (int i = 0; i < epcsInPackage; i++)
            {
                byte infoLength = payl[index];
                byte[] temp = new byte[infoLength];
                System.arraycopy(payl, index + 1, temp, 0, infoLength);

                AtomicBoolean ok = new AtomicBoolean(false);
                JrfeTagEvent tagEvent = parseTagEvent(temp, ok);

                if (!ok.get())
                	throw JrfeProtocolException.DataError(_LastSentMessage, payl);

                tagList.add(tagEvent);
                index += payl[index];
                index++;
            }
            epcsReceived += epcsInPackage;

            JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - OK : Package Count(" + epcsInPackage + ")");

            if (epcsReceived >= epcCount)
                break;
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - OK : Count(" + epcCount + ")");
        for(int i = 0; i < tagList.size(); i++)
        {	
        	JrfeTagEvent t = tagList.get(i);
            JrfeGlobal.trc(_MinimumTraceLevel, "Single Inventory - OK :     EPC: " + JrfeHelper.toHexString(t.tagId));
        }

        return tagList;
    }

    /**
     * Sets the cyclic inventory on or off
     * @param on		Cyclic inventory mode
     * @throws JrfeProtocolException
     */
    public void setCyclicInventory(boolean on) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Cyclic Inventory - Trying to set cyclic inventory to " + on);

        byte[] payload;
        payload = new byte[1];
        if (on)
	        payload[0] = (byte) JrfeConstants.eRFE_INVENTORY_MODE.INVENTORY_ON;
        else
            payload[0] = (byte) JrfeConstants.eRFE_INVENTORY_MODE.INVENTORY_OFF;

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_INVENTORY_CYCLIC, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Cyclic Inventory - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_INVENTORY_CYCLIC), _ResponseTimeOut, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Cyclic Inventory - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Cyclic Inventory - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Cyclic Inventory - OK");

        return;
    }

    /**
     * Reads data from a tag.
     * @param epc			EPC of the specified tag
     * @param mem_bank		Memory bank where to read data from
     * @param address		Address within the memory bank
     * @param passwd		The access password to read from the tag
     * @param count			The count of data that should be read
     * @return			The read data
     * @throws JrfeProtocolException
     */
    public byte[] readFromTag(byte[] epc, byte mem_bank, Short address, byte[] passwd, byte count) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - Trying to read from tag " + JrfeHelper.toHexString(epc) + " from memory bank "
		        + mem_bank + " and address " + address + " the count " + count);

        byte[] data = null;

        if (passwd.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Data");
			throw JrfeProtocolException.DataInputError("Password");
        }


        byte[] payload;
        List<Byte> payloadList = new ArrayList<Byte>();
        payloadList.add((byte) epc.length);
        JrfeHelper.addRange(epc, payloadList);

        payloadList.add((byte) mem_bank);
        payloadList.add((byte) (address >> 8));
        payloadList.add((byte) (address >> 0));
        JrfeHelper.addRange(passwd, payloadList);

        payloadList.add(count);
        payload = JrfeHelper.toByteArray(payloadList);

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_READ_FROM_TAG, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_READ_FROM_TAG), _ResponseTimeOut * 4, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || ((payl[0])
                != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS) && ((payl[0])
                != JrfeConstants.eRFE_RET_VALUE.RFE_RET_RESULT_PENDING))
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        if ((payl[0]) == JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
            data = new byte[payl[1]];
            System.arraycopy(payl, 2, data, 0, payl[1]);
        }
        else if ((payl[0]) == JrfeConstants.eRFE_RET_VALUE.RFE_RET_RESULT_PENDING)
        {
            if (payl.length != 2)
	        {
		        JrfeGlobal.trc(0, "Read From Tag - NOK - Payl");
		        _LastReturnCode =  payl[0];
		        throw JrfeProtocolException.DataError(_LastSentMessage, payl);
	        }

            AtomicBoolean p_result = new AtomicBoolean(false);
            byte[] pendingResp = _PendingMessageQueue.waitForMessage(payl[1], _ResponseTimeOut * 10, p_result);
            if (!p_result.get())
            {
                JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Resp");
                throw JrfeProtocolException.ReceiveError(_LastSentMessage);
            }

            if (pendingResp.length == 0 || (pendingResp[0])
                != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
            {
                JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - NOK - Payl");
                _LastReturnCode = pendingResp[0];
                if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
    	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
    	        else
    	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
            }

            data = new byte[pendingResp[1]];
            System.arraycopy(pendingResp, 2, data, 0, pendingResp[1]);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Read From Tag - OK : Read the data from the tag:" + JrfeHelper.toHexString(data));

        return data;
    }

    /**
     * Writes data to the a tag.
     * @param epc			EPC of the specified tag
     * @param mem_bank		Memory bank where data should be written to
     * @param address		Address within the memory bank
     * @param passwd		The access password to write to the tag
     * @param data			The data that should be written
     * @throws JrfeProtocolException
     */
    public void writeToTag(byte[] epc, byte mem_bank, Short address, byte[] passwd, byte[] data) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - Trying to write to tag " + JrfeHelper.toHexString(epc) + " at bank "
		        + mem_bank + " and address " + address + " the bytes " + JrfeHelper.toHexString(data));

        if (passwd.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - NOK - Data");
			throw JrfeProtocolException.DataInputError("Password");
        }

        byte[] payload;
        ArrayList<Byte> payloadList = new ArrayList<Byte>();
        payloadList.add((byte)epc.length);
        JrfeHelper.addRange(epc, payloadList);

        payloadList.add((byte) mem_bank);
        payloadList.add((byte) (address >> 8));
        payloadList.add((byte) (address >> 0));
        JrfeHelper.addRange(passwd, payloadList);

        payloadList.add((byte)data.length);
        JrfeHelper.addRange(data, payloadList);

        payload = JrfeHelper.toByteArray(payloadList);

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_WRITE_TO_TAG, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_WRITE_TO_TAG), _ResponseTimeOut * 4, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Write To Tag - OK");

        return;
    }

    /**
     * Locks a specified memory region of a tag.
     * @param epc			EPC of the specified tag
     * @param mode			The lock mode
     * @param memory		The memory region
     * @param password		The access password to lock the tag
     * @throws JrfeProtocolException
     */
    public void lockTag(byte[] epc, byte mode, byte memory, byte[] password) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - Trying to lock tag " + JrfeHelper.toHexString(epc) + " with the mode "
		        + mode + " the memory " + memory);

        if (password.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - NOK - Data");
			throw JrfeProtocolException.DataInputError("Password");
        }

        byte[] payload;
        ArrayList<Byte> payloadList = new ArrayList<Byte>();
        payloadList.add((byte) epc.length);
        JrfeHelper.addRange(epc, payloadList);

        payloadList.add((byte) mode);
        payloadList.add((byte) memory);
        JrfeHelper.addRange(password, payloadList);

        payload = JrfeHelper.toByteArray(payloadList);

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_LOCK_TAG, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_LOCK_TAG), _ResponseTimeOut * 2, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Lock Tag - OK");

        return;
    }

    /**
     * Kills a tag
     * @param epc		EPC of the specified tag
     * @param rfu		rfu
     * @param recom		recom
     * @param password	The kill password to kill the tag
     * @throws JrfeProtocolException
     */
    public void killTag(byte[] epc, byte rfu, byte recom, byte[] password) throws JrfeProtocolException
    {
        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - Trying to kill tag " + JrfeHelper.toHexString(epc) + " with the rfu "
		        + rfu + " the recom " + recom);

        if (password.length != 4){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - NOK - Data");
			throw JrfeProtocolException.DataInputError("Password");
        }

        byte[] payload;
        ArrayList<Byte> payloadList = new ArrayList<Byte>();
        payloadList.add((byte)epc.length);
        JrfeHelper.addRange(epc, payloadList);
        
        payloadList.add( (byte) (  ( (byte) ((rfu & 0x0F) << 4) ) | ( (byte) (recom & 0x0F) )  )  );
        JrfeHelper.addRange(password, payloadList);

        payload = JrfeHelper.toByteArray(payloadList);

        _LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

        // send the command
        boolean res = send2Reader(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_KILL_TAG, payload);
        if (res != true){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - NOK - Send");
	        throw JrfeProtocolException.SendError(_LastSentMessage);
        }

        // wait for either the response or a timeout
        AtomicBoolean result = new AtomicBoolean(false);
        byte[] payl = _MessageQueue.waitForMessage(messageId(JrfeConstants.RFE_COM1_TAG_FUNCTIONS,
		        JrfeConstants.RFE_COM2_KILL_TAG), _ResponseTimeOut * 2, result);
        if (!result.get()){
	        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - NOK - Resp");
	        throw JrfeProtocolException.ReceiveError(_LastSentMessage);
        }

        // parse the response
        if (payl.length == 0 || (payl[0])
		        != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
        {
	        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - NOK - Payl");
	        _LastReturnCode =  payl[0];
	        if((payl[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        	throw JrfeProtocolException.ErrorReceived(_LastSentMessage, payl, _LastReturnCode);
	        else
	        	throw JrfeProtocolException.DataError(_LastSentMessage, payl);
        }

        JrfeGlobal.trc(_MinimumTraceLevel, "Kill Tag - OK");

        return;
    }
    
    /********************************************************************************************* DataListener*/

    /**
     * The listener for events
     */
    private Listener m_listener = null;
    
    public interface Listener
    {
        /**
         * Called whenever a heartbeat event is received.
         * @param data 				Data sent with the heartbeat event
         */
        public void _onHeartBeat(byte[] data);
    	
    	/**
    	 * Called whenever a tag event is received.
    	 * @param tagEvent 			The tag event
    	 */
    	public void _onCyclicInventory(JrfeTagEvent tagEvent);

    	/**
    	 * Called whenever a state change event is received.
    	 * @param newState 			The new state
    	 */
    	public void _onStateChanged(int newState);
    	
    	/**
    	 * Called whenever a status register change event is received.
    	 * @param statusRegister	The new value of the status register
    	 */
    	public void _onStatusRegisterChanged(Long statusRegister);
    	
    	/**
    	 * Called whenever a gpio value change event is received.
    	 * @param gpioValues		The new gpio values
    	 */
    	public void _onGpioValuesChanged(Integer gpioValues);
    	
    	/**
    	 * Called whenever a notification event is received.
    	 * @param payload			The payload for the notification
    	 */
    	public void _onNotification(byte[] payload);
    	
    	/**
    	 * Called whenever an application event is received.
    	 * @param payload 			The payload for the application event
    	 */
    	public void _onApplicationEvent(byte[] payload);
    }
    
    /**
     * Sets the listener
     * @param listener		The listener
     */
    public synchronized void setListener(Listener listener) {
    	m_listener = listener;
    }

    /**
     * Returns the listener
     * @return	The listener
     */
    public synchronized Listener getListener() {
        return m_listener;
    }

    
    /********************************************************************************************* Output Creator */
    
    /**
     * Builds up a message that is sent to the reader
     * @param com1 			Command byte 1
     * @param com2 			Command byte 2
     * @returns 			Success of the operation
     */
    boolean send2Reader(byte com1, byte com2)
    {
        byte[] msg = new byte[10];
        msg[ 0] = JrfeConstants.RFE_START_BYTE_1;
        msg[ 1] = JrfeConstants.RFE_START_BYTE_2;
        msg[ 2] = JrfeConstants.RFE_START_BYTE_3;
        msg[ 3] = JrfeConstants.RFE_COMMAND_START_BYTE;
        msg[ 4] = com1;
        msg[ 5] = com2;
        msg[ 6] = JrfeConstants.RFE_LENGTH_START_BYTE;
        msg[ 7] = 0;
        msg[ 8] = JrfeConstants.RFE_CHECKSUM_START_BYTE;
        msg[ 9] = calcXORCS(msg);

        _MessageQueue.clearMessage(messageId(com1, com2));
        
        _LastSentMessage = new byte[msg.length];
        System.arraycopy(msg,  0,  _LastSentMessage,  0,  msg.length);

        JrfeGlobal.trc(8, "<- SinglMessage " + JrfeHelper.toHexString(msg));

        return _Device.send(msg);
    }
    

    /**
     * Builds up a message that is sent to the reader
     * @param com1 		Command byte 1
     * @param com2 		Command byte 2
     * @param payload 	Paylod of the message
     * @return			Success of the operation
     */
    boolean send2Reader(byte com1, byte com2, byte[] payload)
    {
        ArrayList<Byte> msg = new ArrayList<Byte>();
        msg.add(JrfeConstants.RFE_START_BYTE_1);
        msg.add(JrfeConstants.RFE_START_BYTE_2);
        msg.add(JrfeConstants.RFE_START_BYTE_3);
        msg.add(JrfeConstants.RFE_COMMAND_START_BYTE);
        msg.add(com1);
        msg.add(com2);
        msg.add(JrfeConstants.RFE_LENGTH_START_BYTE);
        msg.add((byte)payload.length);

        if (payload.length > 0)
        {
            msg.add(JrfeConstants.RFE_PAYLOAD_START_BYTE);
            JrfeHelper.addRange(payload, msg);
        }

        msg.add(JrfeConstants.RFE_CHECKSUM_START_BYTE);
        msg.add(calcXORCS(JrfeHelper.toByteArray(msg)));

        _MessageQueue.clearMessage(messageId(com1, com2));
        
        byte[] _msg = JrfeHelper.toByteArray(msg);

        _LastSentMessage = new byte[_msg.length];
        System.arraycopy(_msg,  0,  _LastSentMessage,  0,  _msg.length);

        JrfeGlobal.trc(8, "<- SinglMessage " + JrfeHelper.toHexString(_msg));

        return _Device.send(_msg);
    }
    
    
    
    /********************************************************************************************* Input Creator */
    
    /**
     * Parses the recieved paylod for the heartbeat interrupt
     * @param payload	The received payload
     */
    private void heartBeatISR(byte[] payload)
    {
        if (payload.length < 1 || (payload[0]) != JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS)
	        return;

        if (getListener() != null)
        {
            byte[] data = new byte[payload.length-1];
            System.arraycopy(payload, 1, data, 0, payload.length - 1);
            getListener()._onHeartBeat(data);
        }
    }
        
    /**
     * Parses the recieved paylod for the inventory interrupt
     * @param payload	The received payload
     */
    private void cyclicInventoryISR(byte[] payload)
    {
        if (_BlockCyclicInventoryInterrupts == true)
            return;

        AtomicBoolean ok = new AtomicBoolean(false);
        JrfeTagEvent tagEvent = parseTagEvent(payload, ok);

        if (!ok.get())
            return;

        if(getListener() != null)
        	getListener()._onCyclicInventory(tagEvent);
    }
    
    /**
     * Parses the recieved payload for the state changed interrupt
     * @param payload 	The received payload
     */
    private void stateChangedISR(byte[] payload)
    {
        if (payload.length != 1)
	        return;

        Integer state = JrfeHelper.toInteger(payload,  0,  1);

        if(getListener() != null) 
        	getListener()._onStateChanged(state);
    }
    
    /**
     * Parses the recieved payload for the status register changed interrupt
     * @param payload 	The received payload
     */
    private void statusRegChangedISR ( byte[] payload )
    {
        if (payload.length != 8)
	        return;

        Long statusRegister = JrfeHelper.toLong(payload, 0, 8);

        if(getListener() != null)
        	getListener()._onStatusRegisterChanged(statusRegister);
    }
    
    /**
     * Parses the recieved payload for the gpio values changed interrupt
     * @param payload 	The received payload
     */
    private void gpioValuesChangedISR ( byte[] payload )
    {
        if (payload.length != 4)
	        return;

        Integer gpioValues = JrfeHelper.toInteger(payload, 0, 4);

        if (getListener() != null)
        	getListener()._onGpioValuesChanged(gpioValues);
    }
    
    /**
     * Parses the received palyoad for the notification data
     * @param payload	The received payload
     */
    protected void notificationISR(byte[] payload)
    {
        if (payload.length < 1)
            return;

        if (getListener() != null)
        	getListener()._onNotification(payload);
    }
    
    /**
     * Parses the recieved payload for the application ISR
     * @param payload	The received payload
     */
    private void aplicationISR(byte[] payload)
    {
        if (payload.length < 1)
            return;

        if (getListener() != null)
        	getListener()._onApplicationEvent(payload);
    }
    
    /**
     * Parses the recieved payload for the operation result
     * @param payload 	The received payload
     */
    private void operationResultISR(byte[] payload)
    {
        if (payload.length < 2)
                return;

        byte id = (byte)payload[0];
        byte[] data = new byte[payload.length -1];
        System.arraycopy(payload, 1, data, 0, payload.length - 1);

        _PendingMessageQueue.enqueueMessage(id, data);
    }


    
    /** State of the state machine to parse received data */
    private JrfeConstants.eMessageState     _state = JrfeConstants.eMessageState.START_BYTE_1;
    /** Current single message */
    private ArrayList<Byte>            	_singleMsg = new ArrayList<Byte>();
    /** Current payload index */
    private int                         _payloadIndex = 0;
    /** Current paylod length */
    private int                         _payloadLength = 0;
    
    
    /**
     * Parses a tag event out of a payload part
     * @param payload 	Payload part
     * @param ok 		Result of parsing
     * @return 	Returns the parsed tag event
     */
    private JrfeTagEvent parseTagEvent(byte[] payload, AtomicBoolean ok)
    {
        ok.set(false);

        JrfeConstants.eInventoryMessageState state = JrfeConstants.eInventoryMessageState.START;

        JrfeTagEvent tagEvent = new JrfeTagEvent();

        byte tagIdIndex = 0;
        byte memIndex = 0;
        byte appInfoIndex = 0;

        for(int i = 0; i < payload.length; i++)
        {
        	byte c = payload[i];
            switch (state)
            {
                case START:
                    if (c == JrfeConstants.RFE_TAG_ID_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.TAGID_LENGTH;
                    else if (c == JrfeConstants.RFE_RSSI_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.RSSI1;
                    else if (c == JrfeConstants.RFE_MEM_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.MEM_BANK;
                    else if (c == JrfeConstants.RFE_TRIGGER_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.TRIGGER;
                    else if (c == JrfeConstants.RFE_ANTENNA_ID_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.ANTENNA;
                    else if (c == JrfeConstants.RFE_READ_FREQU_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.FREQUENCY1;
                    else if (c == JrfeConstants.RFE_GEN2_HANDLE_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.HANDLE1;
                    else if (c == JrfeConstants.RFE_STATE_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.STATE1;
                    else if (c == JrfeConstants.RFE_BATTERY_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.BATTERY;
                    else if (c == JrfeConstants.RFE_GEN2_PC_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.PC1;
                    else if (c == JrfeConstants.RFE_MESSAGE_ID_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.MESSAGE_ID;
                    else if (c == JrfeConstants.RFE_APPLICATION_START_BYTE)
                        state = JrfeConstants.eInventoryMessageState.APPLICATION_SIZE;
                    else
                        state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case TAGID_LENGTH:
                    tagEvent.tagId = new byte[c];
                    if (c > 0)
                        state = JrfeConstants.eInventoryMessageState.TAGID;
                    else
                        state = JrfeConstants.eInventoryMessageState.START;
                    break;
                case TAGID:
                    tagEvent.tagId[tagIdIndex++] = c;
                    if (tagEvent.tagId.length == tagIdIndex)
                        state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case RSSI1:
                    tagEvent.rssi = new byte[2];
                    tagEvent.rssi[0] = c;
                    state = JrfeConstants.eInventoryMessageState.RSSI2;
                    break;
                case RSSI2:
                    tagEvent.rssi[1] = c;
                    tagEvent.hasRSSI = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case MEM_BANK:
                    tagEvent.memBank = c;
                    state = JrfeConstants.eInventoryMessageState.MEM_ADDR1;
                    break;
                case MEM_ADDR1:
                    tagEvent.memAddr = 0;
                    tagEvent.memAddr += (short)(((0xFF & c)) << 8);
                    state = JrfeConstants.eInventoryMessageState.MEM_ADDR2;
                    break;
                case MEM_ADDR2:
                    tagEvent.memAddr += c;
                    state = JrfeConstants.eInventoryMessageState.MEM_SIZE;
                    break;
                case MEM_SIZE:
                    tagEvent.memData = new byte[c];
                    state = JrfeConstants.eInventoryMessageState.MEM_DATA;
                    break;
                case MEM_DATA:
                    tagEvent.memData[memIndex++] = c;
                    if (tagEvent.memData.length == memIndex)
                    {
                        state = JrfeConstants.eInventoryMessageState.START;
                        tagEvent.hasMemory = true;
                    }
                    break;

                case TRIGGER:
                    tagEvent.trigger = c;
                    tagEvent.hasTrigger = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case ANTENNA:
                    tagEvent.antennaId = c;
                    tagEvent.hasAntenna = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case FREQUENCY1:
                    tagEvent.readFrequency = 0;
                    tagEvent.readFrequency += (((int)(0xFF & c)) << 16);
                    state = JrfeConstants.eInventoryMessageState.FREQUENCY2;
                    break;
                case FREQUENCY2:
                    tagEvent.readFrequency += (((int)(0xFF & c)) << 8);
                    state = JrfeConstants.eInventoryMessageState.FREQUENCY3;
                    break;
                case FREQUENCY3:
                    tagEvent.readFrequency += (((int)(0xFF & c)) << 0);
                    tagEvent.hasReadFrequency = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case STATE1:
                    tagEvent.state = 0;
                    tagEvent.state += (short)(((0xFF & c)) << 8);
                    state = JrfeConstants.eInventoryMessageState.STATE2;
                    break;
                case STATE2:
                    tagEvent.state += (short)(((0xFF & c)) << 0);
                    tagEvent.hasState = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case HANDLE1:
                    tagEvent.handle = new byte[2];
                    tagEvent.handle[0] = c;
                    state = JrfeConstants.eInventoryMessageState.HANDLE2;
                    break;
                case HANDLE2:
                    tagEvent.handle[1] = c;
                    tagEvent.hasHandle = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case BATTERY:
                    tagEvent.battery = c;
                    tagEvent.hasBattery = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case PC1:
                    tagEvent.pc = new byte[2];
                    tagEvent.pc[0] = c;
                    state = JrfeConstants.eInventoryMessageState.PC2;
                    break;
                case PC2:
                    tagEvent.pc[1] = c;
                    tagEvent.hasPC = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case MESSAGE_ID:
                    tagEvent.messageId = c;
                    tagEvent.hasMessageId = true;
                    state = JrfeConstants.eInventoryMessageState.START;
                    break;

                case APPLICATION_SIZE:
                    tagEvent.applicationInfo = new byte[c];
                    state = JrfeConstants.eInventoryMessageState.APPLICATION_DATA;
                    break;
                case APPLICATION_DATA:
                    tagEvent.applicationInfo[appInfoIndex++] = c;
                    if (tagEvent.applicationInfo.length == appInfoIndex)
                    {
                        state = JrfeConstants.eInventoryMessageState.START;
                        tagEvent.hasApplicationInfo = true;
                    }
                    break;
			
                default:
					return null;
            }
        }

        if (tagEvent.tagId.length == 0)
            return null;

        ok.set(true);

        return tagEvent;
    }

    /**
     * Parses the received bytes from the device and splits it up to single messages.
     * @param data 			Data that where sent by the device
	 */
    private void parseData(byte[] data)
    {
        JrfeGlobal.trc(10, "-> RawMessage: " + JrfeHelper.toHexString(data));

        for(int i = 0; i < data.length; i++)
        {
        	byte c = data[i];
            switch (_state)
            {
                case START_BYTE_1:
                    if (c == JrfeConstants.RFE_START_BYTE_1)
                    {
                        _state = JrfeConstants.eMessageState.START_BYTE_2;
                        _singleMsg.clear();
                        _payloadIndex = 0;
                        _payloadLength = 0;
                    }
                    break;

                case START_BYTE_2:
                    if (c == JrfeConstants.RFE_START_BYTE_2)
                        _state = JrfeConstants.eMessageState.START_BYTE_3;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case START_BYTE_3:
                    if (c == JrfeConstants.RFE_START_BYTE_3)
                        _state = JrfeConstants.eMessageState.COMMAND_START_BYTE;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case COMMAND_START_BYTE:
                    if (c == JrfeConstants.RFE_COMMAND_START_BYTE)
                        _state = JrfeConstants.eMessageState.COMMAND_1;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case COMMAND_1:
                    _state = JrfeConstants.eMessageState.COMMAND_2;
                    break;

                case COMMAND_2:
                    _state = JrfeConstants.eMessageState.LENGTH_START_BYTE;
                    break;

                case LENGTH_START_BYTE:
                    if (c == JrfeConstants.RFE_LENGTH_START_BYTE)
                        _state = JrfeConstants.eMessageState.LENGTH;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case LENGTH:
                    _payloadLength = c;
                    _payloadIndex = 0;
                    if (_payloadLength == 0)
                        _state = JrfeConstants.eMessageState.CHECKSUM_START_BYTE;
                    else
                        _state = JrfeConstants.eMessageState.PAYLOAD_START_BYTE;
                    break;

                case PAYLOAD_START_BYTE:
                    if (c == JrfeConstants.RFE_PAYLOAD_START_BYTE)
                        _state = JrfeConstants.eMessageState.PAYLOAD;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case PAYLOAD:
                    if (++_payloadIndex >= _payloadLength)
                        _state = JrfeConstants.eMessageState.CHECKSUM_START_BYTE;

                    break;

                case CHECKSUM_START_BYTE:
                    if (c == JrfeConstants.RFE_CHECKSUM_START_BYTE)
                        _state = JrfeConstants.eMessageState.CHECKSUM;
                    else
                        _state = JrfeConstants.eMessageState.START_BYTE_1;
                    break;

                case CHECKSUM:
				{
				    _state = JrfeConstants.eMessageState.START_BYTE_1;
				    byte[] msg = JrfeHelper.toByteArray(_singleMsg);
				    if (c != calcXORCS(msg))
				    {
				        JrfeGlobal.trc(0, "CHECKSUM NOK!!");
				        break;
				    }
				
				    JrfeGlobal.trc(9, "-> SingleMessage: " + JrfeHelper.toHexString(msg));
				
				    computeMessage(msg);
				
				    break;
				}

            }
            _singleMsg.add(c);
        }

    }

    /**
     * Computes a single message and stores it in the message queue or computes it.
     * @param msg		A single message from the reader
     */
    private void computeMessage(byte[] msg)
    {
        byte command1 = msg[JrfeConstants.RFE_COMMAND_INDEX_1];
        byte command2 = msg[JrfeConstants.RFE_COMMAND_INDEX_2];

        byte[] payload = new byte[msg[JrfeConstants.RFE_LENGTH_INDEX]];
        System.arraycopy(msg, JrfeConstants.RFE_PAYLOAD_INDEX, payload, 0, msg[JrfeConstants.RFE_LENGTH_INDEX]);
        
        switch (command1)
        {
            case JrfeConstants.RFE_COM1_INTERRUPT: // Interrupts
                switch (command2)
                {
                    case JrfeConstants.RFE_COM2_HEARTBEAT_INTERRUPT:
                        heartBeatISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_INVENTORY_CYCLIC_INTERRUPT:
                        cyclicInventoryISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_STATE_CHANGED_INTERRUPT:
                        stateChangedISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_STATUS_REG_CHANGED_INTERRUPT:
                        statusRegChangedISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_GPIO_PINS_CHANGED:
                        gpioValuesChangedISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_NOTIFICATION_INTERRUPT:
                        notificationISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_APPLICATION_INTERRUPT:
                        aplicationISR(payload);
                        break;
                    case JrfeConstants.RFE_COM2_OPERATION_RESULT_INTERRUPT:
                        operationResultISR(payload);
                        break;

                    default:
                        _MessageQueue.enqueueMessage(messageId(command1, command2), payload);
                        break;
                }
                break;
            default:
                _MessageQueue.enqueueMessage(messageId(command1, command2), payload);
                break;
        }
    }
    

    
    /********************************************************************************************* Helper */

  	/**
     * Generates a unique message id for the command bytes
     * @param command1 		Command byte 1
     * @param command2 		Command byte 2
     * @returns 			Unique message id
	 */
    private int messageId(byte command1, byte command2)
    {
        return (((int) (0xFF & command1)) << 8) | (0xFF & command2);
    }

    /**
     * Calculates the XOR checksum for the given data.
     * @param data 			The data to calc checksum
     * @returns 			The XOR checksum
	 */
    private byte calcXORCS(byte[] data)
    {
        byte result = 0;
        for(int i = 0; i < data.length; i++) 
        {
        	byte c = data[i];
            result ^= c;
        }
        return result;
    }

    
    /**
     * Private member to store received messages according to the message id.
     */
    private JrfeMessageQueue					_MessageQueue = new JrfeMessageQueue();
    
    /**
     * Private member to store received pending messages according to the message id.
     */
    private JrfeMessageQueue					_PendingMessageQueue = new JrfeMessageQueue();
    
    /**
     * Instance of the device that is used to communicate with the reader. 
     */
    private IProtocolDeviceInterface      		_Device;
    
    /**
     * Implementation of the device listener
     */
    private IProtocolDeviceInterface.Listener 	_DeviceListener = new IProtocolDeviceInterface.Listener() {

		@Override
		public void onRunError(final Exception e) {
		}

		@Override
		public void onNewData(final byte[] data) {
			parseData(data);
		}

        @Override
        public void onConnectionOpened(boolean success) {
        }

        @Override
		public void onConnectionClosed() {
		}
	};
    
    /**
     * The last return code of the reader.
     */
	protected int		 						_LastReturnCode = JrfeConstants.eRFE_RET_VALUE.RFE_RET_SUCCESS;

    /**
     * The last sent message
     */
    protected byte[]							_LastSentMessage = null;

    /**
     * The time out in ms to wait for the response of the reader.
     */
    protected int								_ResponseTimeOut = 1000;

    /**
     * Returns Minimum Trace Level
     * @return Minimum Trace Level
     */
    public int getMinimumTraceLevel() {
        return _MinimumTraceLevel;
    }

    /**
     * Sets Minimum Trace Level
     * @param _MinimumTraceLevel    Minimum used trace level
     */
    public void setMinimumTraceLevel(int _MinimumTraceLevel) {
        this._MinimumTraceLevel = _MinimumTraceLevel;
    }

    /**
     * Minimum Trace Level used in component
     */
    protected int                               _MinimumTraceLevel = 3;

    /**
     * Option to block interrupts from cyclic inventory.
     */
    protected boolean 							_BlockCyclicInventoryInterrupts = false;
    
    /**
     * Sets the block value
     * @param block		Defines if the cyclic inventory interrupts should e blocked
     */
    public void setBlockCyclicInventoryInterrupts(boolean block)
    {
    	_BlockCyclicInventoryInterrupts = block;
    }

}
